﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using Lwllm.ArcengineProxy.Common;
using Lwllm.ArcengineProxy.Core.Annotations;
using Lwllm.ArcengineProxy.Core.Models;

namespace Lwllm.ArcengineProxy.Core.Proxys
{
    /// <summary>
    ///     feature的代理实现类
    /// </summary>
    public class FeatureProxy : IFeature, IDisposable
    {
        private IFeature _feature;


        private static Dictionary<string, Dictionary<int, PropertyInfo>> _featureClassFieldsCache =
            new Dictionary<string, Dictionary<int, PropertyInfo>>();


        [ArcengineMember]
        public IFeature Feature
        {
            get { return _feature; }
        }

        [ArcengineMember]
        public FeatureClassProxy FeatureClassProxy { get; }

        [ArcengineMember]
        public FeatureProxy(IFeature feature, FeatureClassProxy featureClassProxy)
        {
            _feature = feature;
            FeatureClassProxy = featureClassProxy;
        }

        ~FeatureProxy()
        {
            try
            {
                this.Dispose();
            }
            catch
            {
                //ignore
            }
        }

        public void Dispose()
        {
            _feature?.ComDispose();
            _feature = null;
            Console.WriteLine("FeatureProxy已被释放");
        }

        [ArcengineMember]
        void IRow.Store()
        {
            _feature.Store();
        }

        [ArcengineMember]
        void IFeature.Delete()
        {
            _feature.Delete();
        }

        [ArcengineMember]
        void IFeature.Store()
        {
            _feature.Store();
        }

        [ArcengineMember]
        void IObject.Delete()
        {
            _feature.Delete();
        }

        [ArcengineMember]
        void IObject.Store()
        {
            _feature.Store();
        }

        [ArcengineMember]
        void IRow.Delete()
        {
            _feature.Delete();
        }

        [ArcengineMember]
        object IRowBuffer.get_Value(int index)
        {
            return _feature.Value[index];
        }

        [ArcengineMember]
        void IFeature.set_Value(int index, object value)
        {
            _feature.Value[index] = value;
        }

        [ArcengineMember] IFields IFeature.Fields => _feature.Fields;

        [ArcengineMember] bool IFeature.HasOID => _feature.HasOID;

        [ArcengineMember] int IFeature.OID => _feature.OID;

        [ArcengineMember] ITable IFeature.Table => _feature.Table;

        [ArcengineMember] IObjectClass IFeature.Class => _feature.Class;

        [ArcengineMember] public IGeometry ShapeCopy => _feature.ShapeCopy;

        [ArcengineMember]
        public IGeometry Shape
        {
            get => _feature.Shape;
            set => _feature.Shape = value;
        }

        [ArcengineMember] public IEnvelope Extent => _feature.Extent;
        [ArcengineMember] public esriFeatureType FeatureType => _feature.FeatureType;

        [ArcengineMember]
        object IFeature.get_Value(int index)
        {
            return _feature.Value[index];
        }

        [ArcengineMember]
        void IObject.set_Value(int index, object value)
        {
            _feature.Value[index] = value;
        }

        [ArcengineMember] IFields IObject.Fields => _feature.Fields;

        [ArcengineMember] bool IObject.HasOID => _feature.HasOID;

        [ArcengineMember] int IObject.OID => _feature.OID;

        [ArcengineMember] ITable IObject.Table => _feature.Table;

        [ArcengineMember] IObjectClass IObject.Class => _feature.Class;

        [ArcengineMember]
        object IObject.get_Value(int index)
        {
            return _feature.Value[index];
        }

        [ArcengineMember]
        void IRow.set_Value(int index, object value)
        {
            _feature.Value[index] = value;
        }

        [ArcengineMember] IFields IRow.Fields => _feature.Fields;

        [ArcengineMember] bool IRow.HasOID => _feature.HasOID;

        [ArcengineMember] int IRow.OID => _feature.OID;

        [ArcengineMember] ITable IRow.Table => _feature.Table;

        [ArcengineMember]
        object IRow.get_Value(int index)
        {
            return _feature.Value[index];
        }

        [ArcengineMember]
        void IRowBuffer.set_Value(int index, object value)
        {
            _feature.Value[index] = value;
        }

        [ArcengineMember] IFields IRowBuffer.Fields => _feature.Fields;

        #region 扩展方法

        public T GetValue<T>(int index) where T : new()
        {
            if (index >= 0)
            {
                return (T)_feature.Value[index];
            }

            return default(T);
        }

        public object GetValue(int index)
        {
            return _feature.Value[index];
        }

        internal virtual void GenerateMembers()
        {
            var propertyInfos = this.GetType().GetProperties()
                .Where(p => !p.GetCustomAttributes(typeof(UnMappedFieldAttribute), true).Any());
            if (!_featureClassFieldsCache.ContainsKey(FeatureClassProxy.FeatureClassProxyId))
            {
                var dictionary = new Dictionary<int, PropertyInfo>();
                foreach (var propertyInfo in propertyInfos)
                {
                    var fieldNameAttribute = propertyInfo.GetCustomAttribute<FieldNameAttribute>();
                    var fieldName = String.Empty;
                    if (fieldNameAttribute != null)
                    {
                        if (!String.IsNullOrWhiteSpace(fieldNameAttribute.Name))
                        {
                            fieldName = fieldNameAttribute.Name;
                        }
                    }

                    if (String.IsNullOrWhiteSpace(fieldName))
                    {
                        fieldName = propertyInfo.Name;
                    }

                    var index = FeatureClassProxy.FindField(fieldName);
                    if (index >= 0)
                    {
                        dictionary.Add(index, propertyInfo);
                    }
                }

                _featureClassFieldsCache.Add(
                    FeatureClassProxy.FeatureClassProxyId,
                    dictionary);
            }

            foreach (var featureClassField in _featureClassFieldsCache[
                FeatureClassProxy.FeatureClassProxyId])
            {
                var propertyInfo = featureClassField.Value;
                propertyInfo.SetValue(this, GetValue(featureClassField.Key));
            }
        }

        #endregion
    }
}